/**
 * @file events.js 一个事件系统，和jquery的事件类似
 */

import * as DomData from './dom-data'
import * as Guid from './guid'
import log from './log'
import window from 'global/window'
import document from 'global/document'

/**
 * 清除浏览器dom事件监听和触发器
 * @param {*} elem
 * @param {*} type
 */
function _cleanUpEvents (elem, type) {
  const data = DomData.getData(elem)

  if (data.handlers[type].length === 0) {
    delete data.handlers[tyep]

    if (elem.removeEventListener) {
      elem.removeEventListener(type, data.dispatcher, false)
    } else if (elem.detachEvent) {
      elem.detachEvent('on' + type, data.dispatcher)
    }
  }

  if (Object.getOwnPropertyNames(data.handlers).length <= 0) {
    delete data.handlers
    delete data.dispatcher
    delete data.disabled
  }

  if (Object.getOwnPropertyNames(data).length === 0) {
    DomData.removeData(elem)
  }
}

/**
 * 处理多个类型的事件
 * @param {*} fn
 * @param {*} elem
 * @param {*} types
 * @param {*} callback
 */
function _handleMultipleEvents (fn, elem, types, callback) {
  types.forEach(function (type) {
    fn(elem, type, callback)
  })
}

/**
 * 使得native事件有标准的属性值
 * @param {*} event
 */
export function fixEvent (event) {
  function returnTrue () {
    return true
  }
  function returnFalse () {
    return false
  }

  // 如果有必要的话，就fix一下
  if (!event || !event.isPropagationStopped) {
    const old = event || window.event

    event = {}

    // 克隆属性
    for (const key in old) {

      // 在Safari6.0.3中，复制layerX/layerY属性会警告
      // chrome中，复制废弃的keyLocation会警告
      if (
        key !== 'layerX' && key !== 'layerY' && key!== 'keyLOcation'
        && key !== 'webkitMovementX' && key !== 'webkitMovementY'
      ) {
        if (!(key === 'returnValue' && oldPreventDefault)) {
          event[key] = old[key]
        }
      }
    }

    // fix target
    if (!event.target) {
      event.target.event.srcElement || document
    }

    // fix relatedTarget
    if (!event.relatedTarget) {
      event.relatedTarget = event.fromElement === event.target ?
        event.toElement :
        event.fromElement
    }

    // fix 阻止浏览器的默认行为
    event.preventDefault = function () {
      if (old.preventDefault) {
        old.preventDefault()
      }
      event.returnValue = false
      old.returnValue = false
      event.defaultPrevented = true
    }
    event.defaultPrevented = false

    // fix 阻止事件冒泡
    event.stopPropagation = function () {
      if (old.stopPropagation) {
        old.stopPropagation()
      }
      event.cancelBubble = true
      old.cancelBubble = true
      event.isPropagationStopped = returnTrue
    }
    event.isPropagationStopped = returnFalse

    // fix 阻止事件冒泡 和上面有什么关系??
    event.stopImmediatePropagation = function () {
      if (old.stopImmediatePropagation) {
        old.stopImmediatePropagation()
      }
      event.isImmediatePropagationStopped = returnTrue
      event.stopPropagation()
    }
    event.isImmediatePropagationStopped = returnFalse

    // fix 处理鼠标位置
    if (event.clientX !== null && event.clientX !== undefined) {
      const doc = document.documentElement
      const body = document.body

      event.pageX = event.clientX +
        (doc && doc.scrollLeft || body && body.scrollLeft || 0) -
        (doc && doc.clientLeft || body && body.clientLeft || 0)
      event.pageY = event.clientY +
        (doc && doc.scrollTop || body && body.scrollTop || 0) -
        (doc && doc.clientTop || body && body.clientTop || 0)
    }

    // 处理键盘按压
    event.which = event.charCode || event.keyCode

    // 处理按钮的鼠标点击 0==left, 1==middle 2==right
    if (event.button !== null && event.button !== undefined) {
      event.button = (
        event.button & 1 ?
          0 :
          (
            event.button & 4 ?
              1 :
              (event.button & 2 ? 2 :0)
          )
      )
    }
  }

  // 返回fix后的event
  return event
}

/**
 * 是否支持passive事件 ??passive事件是什么
 */
let _supportsPassive = false

(function () {
  try {
    const opts = Object.defineProperty({}, 'passive', {
      get () {
        _supportsPassive = true
      }
    })

    window.addEventListener('test', null, opts)
    window.removeEventListener('test', null, opts)
  }
})()

/**
 * chrome 希望passive的事件
 */
const passiveEvents = [
  'touchstart',
  'touchmove'
]

/**
 * 添加事件
 * @param {*} elem
 * @param {string|string[]} type
 * @param {*} fn
 */
export function on (elem, type, fn) {
  if (Array.isArray(type)) {
    return _handleMultipleEvents(on, elem, type, fn)
  }

  const data = DomData.getData(elem)

  if (!data.handlers) {
    data.handlers = {}
  }

  if (!data.handlers[type]) {
    data.handlers[type] = []
  }

  if (!fn.guid) {
    fn.guid = Guid.newGUID()
  }

  data.handlers[type].push(fn)

  if (!data.dispatcher) {
    data.disabled = false

    data.dispatcher = function (event, hash) {
      if (data.disabled) {
        return
      }

      event = fixEvent(event)

      const handlers = data.handlers[event.type]
      if (handlers) {
        const handlersCopy = handlers.slice(0)
        for (let m = 0, n = handlersCopy.length; m < n; m++) {
          if (event.isImmediatePropagationStopped()) {
            break
          } else {
            try {
              handlersCopy[m].call(elem, event, hash)
            } catch (e) {
              console.error(e)
            }
          }
        }
      }
    }
  }

  if (data.handlers[type].length === 1) {
    if (elem.addEventListener) {
      let options = false

      if (
        _supportsPassive && passiveEvents.indexOf(type) > -1
      ) {
        options = {passive: true}
      }
      elem.addEventListener(type, data.dispatcher, options)
    } else if (elem.attachEvent) {
      elem.attachEvent('on' + type, data.dispatcher)
    }
  }
}


export function off (elem, type, fn) {
  if (!DomData.hasData(elem)) {
    return
  }

  const data = DomData.getData(elem)

  if (!data.handlers) {
    return
  }

  if (Array.isArray(type)) {
    return _handleMultipleEvents(off, elem, type, fn)
  }

  // 辅助函数
  const removeType = function (el, t) {
    data.handlers[t] = []
    _cleanUpEvents(el, t)
  }

  // 移除所有事件
  if (type === undefined) {
    for (const t in data.handlers) {
      if (Object.prototype.hasOwnProperty.call(data.handlers || {}, t)) {
        removeType(elem, t)
      }
    }
    return
  }

  const handlers = data.handlers[type]

  // 没有事件，不操作
  if (!handlers) {
    return
  }

  // 移除该类型的所有事件
  if (!fn) {
    removeType(elem, type)
  }

  // 清除这个guid的事件处理函数
  if (fn.guid) {
    for (let n = 0; n < handlers.length; n++) {
      if (handlers[n].guid === fn.guid) {
        handlers.splice(n--, 1)
      }
    }
  }

  // 如果handlers[type]里没有处理函数就清除这个事件监听
  _cleanUpEvents(elem, type)
}


/**
 * 触发一个事件
 * @param {*} elem
 * @param {*} event
 * @param {*} hash
 * @return {boolean|undefined}
 *    返回defaultPrevented的非值
 *    或者undefined
 */
export function trigger (elem, event, hash) {
  const elemData = (DomData.hasData(elem)) ? DomData.getData(elem) : {}
  const parent = elem.parentNode || elem.ownerDocument

  if (typeof event === 'string') {
    event = { type: event, target: elem }
  } else if (!event.target) {
    event.target = elem
  }

  event = fixEvent(event)

  // 如果有dispatcher就执行
  if (elemData.dispatcher) {
    elemData.dispatcher.call(elem, event, hash)
  }

  // 除非明确停止或者事件根本不冒泡，否则就往上冒泡
  if (parent && !event.isPropagationStopped() && event.bubbles) {
    trigger.call(null, parent, event, hash)

    // 如果dom是最顶端，如果没有禁止就触发默认行为 ??
  } else if (!parent && !event.defaultPrevented) {
    const targetData = DomData.getData(event.target)

    if (event.target[event.type]) {
      targetData.disabled = true

      if (typeof event.target[event.type] === 'function') {
        event.target[event.type]()
      }
      targetData.disabled = false
    }
  }

  return !event.defaultPrevented
}


export function one (elem, type, fn) {
  if (Array.isArray(type)) {
    return _handleMultipleEvents(one, elem, type, fn)
  }
  const func = function () {
    off(elem, type, func)
    fn.apply(this, arguments)
  }
  func.guid = fn.guid = fn.guid || Guid.newGUID()
  on(elem, type, func)
}
